package org.smart4j.framework.helper;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Aspect;
import org.smart4j.framework.annotation.Service;
import org.smart4j.framework.proxy.AspectProxy;
import org.smart4j.framework.proxy.Proxy;
import org.smart4j.framework.proxy.ProxyManager;
import org.smart4j.framework.proxy.TransactionProxy;

/**
 * description: aop helper 获取带有Aspect注解的集合
 * @version v1.0
 * @author w
 * @date 2018年10月2日下午9:38:56
 */
public class AopHelper {
	
	private static final Logger logger = LoggerFactory.getLogger(AopHelper.class);
	
	// 静态块 用于初始整个AOP框架
	static{
		try {
			Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
			Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
			for(Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()){
				Class<?> targetClass = targetEntry.getKey();
				List<Proxy> proxyList = targetEntry.getValue();
				Object proxy = ProxyManager.createProxy(targetClass, proxyList);
				BeanHelper.setBean(targetClass, proxy);
			}
		} catch (Exception e) {
			logger.error("init aop framework failure ",e);
		}
	}
	
	/**
	 * description: 获取代理类和目标类之间的映射关系 。
	 * 	1、 一个代理类可以代理多个目标类。 故是一对多的关系
	 * @return
	 * @throws Exception
	 * Map<Class<?>,Set<Class<?>>>
	 * @version v1.0
	 * @author w
	 * @date 2018年10月2日 下午10:04:42
	 */
	private static Map<Class<?>,Set<Class<?>>> createProxyMap() throws Exception{
		Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();
		
		addAspectProxy(proxyMap);
		addTransactionProxy(proxyMap);
		return proxyMap;
	}

	
	/**
	 * description: 事务代理
	 * @param proxyMap
	 * @return void
	 * @version v1.0
	 * @author w
	 * @date 2018年10月20日 下午10:46:19
	 */
	private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> proxyMap) {
		Set<Class<?>> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class);
		proxyMap.put(TransactionProxy.class, serviceClassSet);
	}


	/**
	 * description: 普通切面代理
	 * @param proxyMap
	 * @return void
	 * @version v1.0
	 * @author w
	 * @date 2018年10月20日 下午10:45:10
	 */
	private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> proxyMap) {
		Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
		for (Class<?> proxyClass : proxyClassSet) {
			if(proxyClass.isAnnotationPresent(Aspect.class)){
				Aspect annotation = proxyClass.getAnnotation(Aspect.class);
				Set<Class<?>> targetClassSet = createTargetClassSet(annotation);
				proxyMap.put(proxyClass, targetClassSet);
			}
		}
		
	}


	/**
	 *description: 获取目标类的注解集合 targetClass
	 *  获取Aspect注解中，设置的注解类，若该注解类不是Aspect类，
	 * 	通过ClassHelper.getClassSetByAnnotation 方法获取相关类的集合
	 * @param aspect
	 * @return
	 * @throws Exception
	 * Set<Class<?>>
	 * @version v1.0
	 * @author w
	 * @date 2018年10月2日 下午9:48:44
	 */
	private static Set<Class<?>> createTargetClassSet(Aspect aspect){
		Set<Class<?>> targetClassSet = new HashSet<>();
		Class<? extends Annotation> annotation = aspect.value();
		if(annotation != null && !annotation.equals(Aspect.class)){
			targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
		}
		return targetClassSet;
	}
	
	
	/**
	 * description: 获取目标类和代理类之间的映射关系 。
	 * @param proxyMap  代理类和目标类之间的映射关系
	 * @return
	 * @throws Exception
	 * Map<Class<?>,List<Proxy>>
	 * @version v1.0
	 * @author w
	 * @date 2018年10月2日 下午10:29:08
	 */
	private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
		Map<Class<?>,List<Proxy>> targetMap = new HashMap<>();
		for(Entry<Class<?>, Set<Class<?>>> proxyEntry : proxyMap.entrySet()){
			// 获取代理类 
			Class<?> proxyClass = proxyEntry.getKey();
			// 获取目标类集合
			Set<Class<?>> targetClassSet = proxyEntry.getValue();
			for (Class<?> targetClass : targetClassSet) {
				Proxy proxy =(Proxy)targetClass.newInstance();
				if(targetMap.containsKey(targetClass)){
					// 目标类map映射中添加 新的代理类
					targetMap.get(targetClass).add(proxy);
				}else{
					List<Proxy> proxyList = new ArrayList<>();
					proxyList.add(proxy);
					targetMap.put(proxyClass, proxyList);
				}
			}
		}
		/*for(Map.Entry<Class<?>, Set<Class<?>>> proxyEntity : proxyMap.entrySet()){
		}*/
		return targetMap;
	}
	
	

}
